﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;

namespace ImageProcessor
{
	public class ConvolutionMatrix
	{
		public int MatrixSize = 3;

		public double[,] Matrix;
		public double Factor = 1;
		public double Offset = 1;

		public ConvolutionMatrix(int size)
		{
			MatrixSize = 3;
			Matrix = new double[size, size];
		}

		public void SetAll(double value)
		{
			for (int i = 0; i < MatrixSize; i++)
			{
				for (int j = 0; j < MatrixSize; j++)
				{
					Matrix[i, j] = value;
				}
			}
		}
	}

	public class ImageProcessor
	{
		private Bitmap bitmapImage;
		public void SetImage (Bitmap b)
		{
			bitmapImage = new Bitmap (b);
		}
		public void SetImage(string path)
		{
			bitmapImage = new Bitmap (path);
		}

		public Bitmap GetImage()
		{
			return bitmapImage;
		}

		public void ApplyInvert()
		{
			byte A, R, G, B;
			Color pixelColor;

			for (int y = 0; y < bitmapImage.Height; y++)
			{
				for (int x = 0; x < bitmapImage.Width; x++)
				{
					pixelColor = bitmapImage.GetPixel (x, y);
					A = pixelColor.A;
					R = (byte) (255 - pixelColor.R);
					G = (byte) (255 - pixelColor.G);
					B = (byte) (255 - pixelColor.B);
					bitmapImage.SetPixel (x, y, Color.FromArgb ((int) A, (int) R, (int) G, (int) B));
				}
			}

		}

		public void ApplyGreyscale()
		{
			byte A, R, G, B;
			Color pixelColor;

			for (int y = 0; y < bitmapImage.Height; y++)
			{
				for (int x = 0; x < bitmapImage.Width; x++)
				{
					pixelColor = bitmapImage.GetPixel (x, y);
					A = pixelColor.A;
					R = (byte) ((0.299 * pixelColor.R) + (0.587 * pixelColor.G) + (0.114 * pixelColor.B));
					G = B = R;

					bitmapImage.SetPixel (x, y, Color.FromArgb ((int) A, (int) R, (int) G, (int) B));
				}
			}

		}



		public void ApplyGamma(double r, double g, double b)
		{
			byte A, R, G, B;
			Color pixelColor;

			byte[] redGamma = new byte[256];
			byte[] greenGamma = new byte[256];
			byte[] blueGamma = new byte[256];

			for (int i = 0; i < 256; ++i)
			{
				redGamma[i] = (byte) Math.Min (255, (int) ((255.0
					* Math.Pow (i / 255.0, 1.0 / r)) + 0.5));
				greenGamma[i] = (byte) Math.Min (255, (int) ((255.0
					* Math.Pow (i / 255.0, 1.0 / g)) + 0.5));
				blueGamma[i] = (byte) Math.Min (255, (int) ((255.0
					* Math.Pow (i / 255.0, 1.0 / b)) + 0.5));
			}

			for (int y = 0; y < bitmapImage.Height; y++)
			{
				for (int x = 0; x < bitmapImage.Width; x++)
				{
					pixelColor = bitmapImage.GetPixel (x, y);
					A = pixelColor.A;
					R = redGamma[pixelColor.R];
					G = greenGamma[pixelColor.G];
					B = blueGamma[pixelColor.B];
					bitmapImage.SetPixel (x, y, Color.FromArgb ((int) A, (int) R, (int) G, (int) B));
				}
			}
		}

		public void ApplyColorFilter(double r, double g, double b)
		{
			byte A, R, G, B;
			Color pixelColor;

			for (int y = 0; y < bitmapImage.Height; y++)
			{
				for (int x = 0; x < bitmapImage.Width; x++)
				{
					pixelColor = bitmapImage.GetPixel (x, y);
					A = pixelColor.A;
					R = (byte) (pixelColor.R * r);
					G = (byte) (pixelColor.G * g);
					B = (byte) (pixelColor.B * b);
					bitmapImage.SetPixel (x, y, Color.FromArgb ((int) A, (int) R, (int) G, (int) B));
				}
			}
		}

		public void ApplySepia(int depth)
		{
			int A, R, G, B;
			Color pixelColor;

			for (int y = 0; y < bitmapImage.Height; y++)
			{
				for (int x = 0; x < bitmapImage.Width; x++)
				{
					pixelColor = bitmapImage.GetPixel (x, y);
					A = pixelColor.A;
					R = (int) ((0.299 * pixelColor.R) + (0.587 * pixelColor.G) + (0.114 * pixelColor.B));
					G = B = R;

					R += (depth * 2);
					if (R > 255)
					{
						R = 255;
					}
					G += depth;
					if (G > 255)
					{
						G = 255;
					}

					bitmapImage.SetPixel (x, y, Color.FromArgb (A, R, G, B));
				}
			}
		}

		public void ApplyDecreaseColourDepth(int offset)
		{
			int A, R, G, B;
			Color pixelColor;

			for (int y = 0; y < bitmapImage.Height; y++)
			{
				for (int x = 0; x < bitmapImage.Width; x++)
				{
					pixelColor = bitmapImage.GetPixel (x, y);
					A = pixelColor.A;
					R = ((pixelColor.R + (offset / 2)) - ((pixelColor.R + (offset / 2)) % offset) - 1);
					if (R < 0)
					{
						R = 0;
					}
					G = ((pixelColor.G + (offset / 2)) - ((pixelColor.G + (offset / 2)) % offset) - 1);
					if (G < 0)
					{
						G = 0;
					}
					B = ((pixelColor.B + (offset / 2)) - ((pixelColor.B + (offset / 2)) % offset) - 1);
					if (B < 0)
					{
						B = 0;
					}
					bitmapImage.SetPixel (x, y, Color.FromArgb (A, R, G, B));
				}
			}

		}

		public void ApplyContrast(double contrast)
		{
			double A, R, G, B;

			Color pixelColor;

			contrast = (100.0 + contrast) / 100.0;
			contrast *= contrast;

			for (int y = 0; y < bitmapImage.Height; y++)
			{
				for (int x = 0; x < bitmapImage.Width; x++)
				{
					pixelColor = bitmapImage.GetPixel (x, y);
					A = pixelColor.A;

					R = pixelColor.R / 255.0;
					R -= 0.5;
					R *= contrast;
					R += 0.5;
					R *= 255;

					if (R > 255)
					{
						R = 255;
					}
					else if (R < 0)
					{
						R = 0;
					}

					G = pixelColor.G / 255.0;
					G -= 0.5;
					G *= contrast;
					G += 0.5;
					G *= 255;
					if (G > 255)
					{
						G = 255;
					}
					else if (G < 0)
					{
						G = 0;
					}

					B = pixelColor.B / 255.0;
					B -= 0.5;
					B *= contrast;
					B += 0.5;
					B *= 255;
					if (B > 255)
					{
						B = 255;
					}
					else if (B < 0)
					{
						B = 0;
					}

					bitmapImage.SetPixel (x, y, Color.FromArgb ((int) A, (int) R, (int) G, (int) B));
				}
			}

		}

		public void ApplyBrightness(int brightness)
		{
			int A, R, G, B;
			Color pixelColor;

			for (int y = 0; y < bitmapImage.Height; y++)
			{
				for (int x = 0; x < bitmapImage.Width; x++)
				{
					pixelColor = bitmapImage.GetPixel (x, y);
					A = pixelColor.A;
					R = pixelColor.R + brightness;
					if (R > 255)
					{
						R = 255;
					}
					else if (R < 0)
					{
						R = 0;
					}

					G = pixelColor.G + brightness;
					if (G > 255)
					{
						G = 255;
					}
					else if (G < 0)
					{
						G = 0;
					}

					B = pixelColor.B + brightness;
					if (B > 255)
					{
						B = 255;
					}
					else if (B < 0)
					{
						B = 0;
					}

					bitmapImage.SetPixel (x, y, Color.FromArgb (A, R, G, B));
				}
			}

		}


		public void ApplySmooth(double weight)
		{
			ConvolutionMatrix matrix = new ConvolutionMatrix (3);
			matrix.SetAll (1);
			matrix.Matrix[1, 1] = weight;
			matrix.Factor = weight + 8;
			bitmapImage = Convolution3x3 (bitmapImage, matrix);

		}

		public void ApplyGaussianBlur(double peakValue)
		{
			ConvolutionMatrix matrix = new ConvolutionMatrix (3);
			matrix.SetAll (1);
			matrix.Matrix[0, 0] = peakValue / 4;
			matrix.Matrix[1, 0] = peakValue / 2;
			matrix.Matrix[2, 0] = peakValue / 4;
			matrix.Matrix[0, 1] = peakValue / 2;
			matrix.Matrix[1, 1] = peakValue;
			matrix.Matrix[2, 1] = peakValue / 2;
			matrix.Matrix[0, 2] = peakValue / 4;
			matrix.Matrix[1, 2] = peakValue / 2;
			matrix.Matrix[2, 2] = peakValue / 4;
			matrix.Factor = peakValue * 4;
			bitmapImage = Convolution3x3 (bitmapImage, matrix);

		}

		public void ApplySharpen(double weight)
		{
			ConvolutionMatrix matrix = new ConvolutionMatrix (3);
			matrix.SetAll (1);
			matrix.Matrix[0, 0] = 0;
			matrix.Matrix[1, 0] = -2;
			matrix.Matrix[2, 0] = 0;
			matrix.Matrix[0, 1] = -2;
			matrix.Matrix[1, 1] = weight;
			matrix.Matrix[2, 1] = -2;
			matrix.Matrix[0, 2] = 0;
			matrix.Matrix[1, 2] = -2;
			matrix.Matrix[2, 2] = 0;
			matrix.Factor = weight - 8;
			bitmapImage = Convolution3x3 (bitmapImage, matrix);

		}

		public void ApplyMeanRemoval(double weight)
		{
			ConvolutionMatrix matrix = new ConvolutionMatrix (3);
			matrix.SetAll (1);
			matrix.Matrix[0, 0] = -1;
			matrix.Matrix[1, 0] = -1;
			matrix.Matrix[2, 0] = -1;
			matrix.Matrix[0, 1] = -1;
			matrix.Matrix[1, 1] = weight;
			matrix.Matrix[2, 1] = -1;
			matrix.Matrix[0, 2] = -1;
			matrix.Matrix[1, 2] = -1;
			matrix.Matrix[2, 2] = -1;
			matrix.Factor = weight - 8;
			bitmapImage = Convolution3x3 (bitmapImage, matrix);

		}

		public void ApplyEmboss(double weight)
		{
			ConvolutionMatrix matrix = new ConvolutionMatrix (3);
			matrix.SetAll (1);
			matrix.Matrix[0, 0] = -1;
			matrix.Matrix[1, 0] = 0;
			matrix.Matrix[2, 0] = -1;
			matrix.Matrix[0, 1] = 0;
			matrix.Matrix[1, 1] = weight;
			matrix.Matrix[2, 1] = 0;
			matrix.Matrix[0, 2] = -1;
			matrix.Matrix[1, 2] = 0;
			matrix.Matrix[2, 2] = -1;
			matrix.Factor = 4;
			matrix.Offset = 127;
			bitmapImage = Convolution3x3 (bitmapImage, matrix);

		}

		public Bitmap Convolution3x3(Bitmap b, ConvolutionMatrix m)
		{
			Bitmap newImg = (Bitmap) b.Clone ();
			Color[,] pixelColor = new Color[3, 3];
			int A, R, G, B;

			for (int y = 0; y < b.Height - 2; y++)
			{
				for (int x = 0; x < b.Width - 2; x++)
				{
					pixelColor[0, 0] = b.GetPixel (x, y);
					pixelColor[0, 1] = b.GetPixel (x, y + 1);
					pixelColor[0, 2] = b.GetPixel (x, y + 2);
					pixelColor[1, 0] = b.GetPixel (x + 1, y);
					pixelColor[1, 1] = b.GetPixel (x + 1, y + 1);
					pixelColor[1, 2] = b.GetPixel (x + 1, y + 2);
					pixelColor[2, 0] = b.GetPixel (x + 2, y);
					pixelColor[2, 1] = b.GetPixel (x + 2, y + 1);
					pixelColor[2, 2] = b.GetPixel (x + 2, y + 2);

					A = pixelColor[1, 1].A;

					R = (int) ((((pixelColor[0, 0].R * m.Matrix[0, 0]) +
								 (pixelColor[1, 0].R * m.Matrix[1, 0]) +
								 (pixelColor[2, 0].R * m.Matrix[2, 0]) +
								 (pixelColor[0, 1].R * m.Matrix[0, 1]) +
								 (pixelColor[1, 1].R * m.Matrix[1, 1]) +
								 (pixelColor[2, 1].R * m.Matrix[2, 1]) +
								 (pixelColor[0, 2].R * m.Matrix[0, 2]) +
								 (pixelColor[1, 2].R * m.Matrix[1, 2]) +
								 (pixelColor[2, 2].R * m.Matrix[2, 2]))
										/ m.Factor) + m.Offset);

					if (R < 0)
					{
						R = 0;
					}
					else if (R > 255)
					{
						R = 255;
					}

					G = (int) ((((pixelColor[0, 0].G * m.Matrix[0, 0]) +
								 (pixelColor[1, 0].G * m.Matrix[1, 0]) +
								 (pixelColor[2, 0].G * m.Matrix[2, 0]) +
								 (pixelColor[0, 1].G * m.Matrix[0, 1]) +
								 (pixelColor[1, 1].G * m.Matrix[1, 1]) +
								 (pixelColor[2, 1].G * m.Matrix[2, 1]) +
								 (pixelColor[0, 2].G * m.Matrix[0, 2]) +
								 (pixelColor[1, 2].G * m.Matrix[1, 2]) +
								 (pixelColor[2, 2].G * m.Matrix[2, 2]))
										/ m.Factor) + m.Offset);

					if (G < 0)
					{
						G = 0;
					}
					else if (G > 255)
					{
						G = 255;
					}

					B = (int) ((((pixelColor[0, 0].B * m.Matrix[0, 0]) +
								 (pixelColor[1, 0].B * m.Matrix[1, 0]) +
								 (pixelColor[2, 0].B * m.Matrix[2, 0]) +
								 (pixelColor[0, 1].B * m.Matrix[0, 1]) +
								 (pixelColor[1, 1].B * m.Matrix[1, 1]) +
								 (pixelColor[2, 1].B * m.Matrix[2, 1]) +
								 (pixelColor[0, 2].B * m.Matrix[0, 2]) +
								 (pixelColor[1, 2].B * m.Matrix[1, 2]) +
								 (pixelColor[2, 2].B * m.Matrix[2, 2]))
										/ m.Factor) + m.Offset);

					if (B < 0)
					{
						B = 0;
					}
					else if (B > 255)
					{
						B = 255;
					}
					newImg.SetPixel (x + 1, y + 1, Color.FromArgb (A, R, G, B));
				}
			}
			return newImg;
		}
	}
}
